import { Pattern, TYPES } from "./EmbroideryPattern";

const rgbToHex = (color) => {
    const componentToHex = (c) => {
        var hex = c.toString(16);
        return hex.length == 1 ? "0" + hex : hex.substring(0, 2);
    };

    if (color) {
        const r = componentToHex(color.r);
        const g = componentToHex(color.g);
        const b = componentToHex(color.b);

        return "#" + r + g + b;
    } else {
        return "#ff0000";
    }
};

const border = 3.8; // 线宽

// 颜色灰度加减
const shadeColor = (color, percent) => {
    const num = parseInt(color.slice(1), 16);
    const amt = Math.round(2.55 * percent);
    const rgb = {
        r: Math.max(0, Math.min(255, (num >> 16) + amt)),
        g: Math.max(0, Math.min(255, ((num >> 8) & 0x00ff) + amt)),
        b: Math.max(0, Math.min(255, (num & 0x0000ff) + amt)),
    };

    return rgbToHex(rgb);
};

// 坐标旋转
// x,y 点的坐标
// xo,yo 镜像原点
// r 旋转角度
const coordinateRotation = (x, y, ox, oy, r) => {
    const radians = (-r * Math.PI) / 180;
    const cos = Math.cos(radians);
    const sin = Math.sin(radians);

    const x1 = x - ox;
    const y1 = y - oy;
    const xr = x1 * cos + y1 * sin;
    const yr = y1 * cos - x1 * sin;

    return {
        x: xr + ox,
        y: yr + oy,
    };
};

// 垂直方向直线渐变
const drawLinearGradient = (ctx, current, previous, bounds, color, scale) => {
    // 向量与夹角
    const cur = { x: current[0] * scale, y: current[1] * scale };
    const prev = { x: previous[0] * scale, y: previous[1] * scale };
    const verX = cur.x - prev.x;
    const verY = cur.y - prev.y;
    const r = ((Math.atan2(verY, verX) / Math.PI) * 180 + 360) % 360;

    // 旋转
    const v1 = coordinateRotation(0.5 * border, 0, 0, 0, r - 90);
    const v2 = coordinateRotation(0.5 * border, 0, 0, 0, r + 90);

    // 偏移
    const left = scale * (bounds.left - 0.5 * border);
    const top = scale * (bounds.top - 0.5 * border);

    // 中点
    const pcx = (cur.x + prev.x) / 2 - left;
    const pcy = (cur.y + prev.y) / 2 - top;

    const pc1 = { x: pcx + v1.x, y: pcy + v1.y };
    const pc2 = { x: pcx + v2.x, y: pcy + v2.y };

    // 四周的点
    const p1 = { x: prev.x + v1.x - left, y: prev.y + v1.y - top };
    const p2 = { x: cur.x + v1.x - left, y: cur.y + v1.y - top };
    const p3 = { x: cur.x + v2.x - left, y: cur.y + v2.y - top };
    const p4 = { x: prev.x + v2.x - left, y: prev.y + v2.y - top };

    // 圆角半径
    const round = 0.38 * border;

    if (color && color.hover) {
        ctx.fillStyle = color.hover;
    } else {
        // 颜色
        const hex = color.hex;

        // 渐变
        const gradient = ctx.createLinearGradient(pc1.x, pc1.y, pc2.x, pc2.y);
        gradient.addColorStop(0, shadeColor(hex, -10));
        gradient.addColorStop(0.1, shadeColor(hex, 10));
        gradient.addColorStop(0.3, shadeColor(hex, 13));
        gradient.addColorStop(1.0, shadeColor(hex, -10));

        ctx.fillStyle = gradient;
    }

    // 画有圆角的线
    ctx.beginPath();
    ctx.moveTo(pc1.x, pc1.y);
    ctx.arcTo(p2.x, p2.y, p3.x, p3.y, round);
    ctx.arcTo(p3.x, p3.y, p4.x, p4.y, round);
    ctx.arcTo(p4.x, p4.y, p1.x, p1.y, round);
    ctx.arcTo(p1.x, p1.y, p2.x, p2.y, round);
    ctx.closePath();
    ctx.fill();
};

export const writeCanvas = (stitches, colors, outer = {}) => {
    const pattern = new Pattern(stitches, colors);
    const canvas = document.createElement("canvas");
    const ctx = canvas.getContext("2d");
    const bounds = pattern.getBounds();

    const blocks = pattern.getAsStitchBlock();
    const scale = Math.max(
        1,
        (outer.width || 0) / bounds.width,
        (outer.height || 0) / bounds.height
    );

    canvas.width = scale * (border + bounds.right - bounds.left);
    canvas.height = scale * (border + bounds.bottom - bounds.top);

    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.lineWidth = border;
    ctx.lineJoin = "round";

    blocks.map((block) => {
        const color = block.color || { hex: "#000" };
        const len = block.stitches.length;

        for (let i = 1; i < len; i++) {
            const current = block.stitches[i];
            const previous = block.stitches[i - 1] || current;
            if (current[0] != previous[0] || current[1] != previous[1]) {
                drawLinearGradient(
                    ctx,
                    current,
                    previous,
                    bounds,
                    color,
                    scale
                );
            }
        }
    });

    return canvas;
};

export const writeSvg = (stitches, colors) => {
    const pattern = new Pattern(stitches, colors);
    const bounds = pattern.getBounds();
    const width = bounds.right - bounds.left;
    const height = bounds.bottom - bounds.top;
    const box = [bounds.left, bounds.top, width, height];

    const blocks = pattern.getAsStitchBlock();
    const length = [];
    const paths = blocks.map((block) => {
        const color = block.color || { hex: "#000" };
        const stitches = block.stitches || [];
        const d = stitches.map((s, i) => (["M"][i] || "L") + s[0] + " " + s[1]);
        const ds = d.join(" ");
        const last = length[length.length - 1];

        if (last && last == color) {
            // 相同
        } else {
            length.push(color);
        }

        return {
            d: ds,
            stoke: color.hex,
            index: length.length - 1,
            stitches: stitches,
            str: `<path fill="none" stroke-width="2" stroke="${color.hex}" d="${ds}" />`,
        };
    });

    const body = paths.map((i) => i.str).join("\n");
    const svg = `<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="${width}px" height="${height}px" viewBox="${box}">${body}</svg>`;

    return {
        left: bounds.left,
        top: bounds.top,
        right: bounds.right,
        bottom: bounds.bottom,
        width: bounds.right - bounds.left,
        height: bounds.bottom - bounds.top,
        paths: paths,
        svg: svg,
    };
};

export const writeHovers = (stitches, colors) => {
    const pattern = new Pattern(stitches, colors);
    const bounds = pattern.getBounds();
    const blocks = pattern.getAsStitchBlock();

    const length = [];
    const paths = blocks.map((block) => {
        const color = block.color || { hex: "#000" };
        const stitches = block.stitches || [];
        const last = length[length.length - 1];

        if (last && last.index == block.index) {
            // 相同
        } else {
            length.push(color);
        }
        return {
            index: length.length - 1,
            stitches: stitches,
        };
    });

    return colors.map((color, index) => {
        const canvas = document.createElement("canvas");
        const ctx = canvas.getContext("2d");
        const blocks = paths.filter((i) => i.index == index);

        canvas.width = border + bounds.right - bounds.left;
        canvas.height = border + bounds.bottom - bounds.top;

        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.lineWidth = border;
        ctx.lineJoin = "round";

        blocks.map((block) => {
            const color = block.color || { hex: "#000" };
            const len = block.stitches.length;

            for (let i = 1; i < len; i++) {
                const current = block.stitches[i];
                const previous = block.stitches[i - 1] || current;
                if (current[0] != previous[0] || current[1] != previous[1]) {
                    drawLinearGradient(
                        ctx,
                        current,
                        previous,
                        bounds,
                        { hover: "#a0fce8" },
                        1
                    );
                }
            }
        });
        return canvas;
    });
};

export const convexHull = (stitches, colors) => {
    const pattern = new Pattern(stitches, colors);
    const bounds = pattern.getBounds();

    const blocks = pattern.getAsStitchBlock();
    const paths = [];

    const convexHull = (points) => {
        points.sort((a, b) => {
            return a.x != b.x ? a.x - b.x : a.y - b.y;
        });

        var n = points.length;
        var hull = [];

        for (var i = 0; i < 2 * n; i++) {
            var j = i < n ? i : 2 * n - 1 - i;
            while (
                hull.length >= 2 &&
                removeMiddle(
                    hull[hull.length - 2],
                    hull[hull.length - 1],
                    points[j]
                )
            )
                hull.pop();
            hull.push(points[j]);
        }

        hull.pop();
        return hull;
    };

    const removeMiddle = (a, b, c) => {
        var cross = (a.x - b.x) * (c.y - b.y) - (a.y - b.y) * (c.x - b.x);
        var dot = (a.x - b.x) * (c.x - b.x) + (a.y - b.y) * (c.y - b.y);
        return cross < 0 || (cross == 0 && dot <= 0);
    };

    blocks.map((block) => {
        const color = block.color || { hex: "#000" };
        const stitches = block.stitches || [];
        const points = stitches.map((s, i) => ({ x: s[0], y: s[1] }));
        const last = paths[paths.length - 1];

        if (last && last.color == color) {
            last.points = last.points.concat(points);
        } else {
            paths.push({
                color: color,
                points: points,
            });
        }
    });

    paths.map((p) => {
        p.hull = convexHull(p.points);
        p.d = p.hull.map((s, i) => (["M"][i] || "L") + s.x + " " + s.y);
    });

    return {
        left: bounds.left,
        top: bounds.top,
        right: bounds.right,
        bottom: bounds.bottom,
        width: bounds.right - bounds.left,
        height: bounds.bottom - bounds.top,
        paths: paths,
    };
};
